package org.scmpatrol.scm.diff;

import java.util.List;

import org.incava.util.diff.Difference;

/**
 * Utility class used to generated diff arborescence between 2 strings
 * representation of a text file (original and result). <br>
 * This class uses the java-diff library
 * (http://www.incava.org/projects/java/java-diff)
 * 
 * @author vincent.sellier
 * 
 */
public class DiffGenerator {
	/**
	 * the original file content
	 */
	private String original;
	/**
	 * the modified file content
	 */
	private String result;

	/**
	 * Constructs the DiffGenerator.
	 * 
	 * @param original
	 *            the original file content
	 * @param result
	 *            the modified file content
	 */
	private DiffGenerator(String original, String result) {
		this.original = original;
		this.result = result;
	}

	/**
	 * Parse original and modified file content to build fine diff. <br>
	 * Diff are based on the java-util library. <br>
	 * 
	 * 
	 * @param original
	 *            original file content
	 * @param result
	 *            modified file content
	 * @return a {@link Diff} object containing differences between original and
	 *         result file content
	 */
	public static Diff generateDiff(String original, String result) {
		DiffGenerator generator = new DiffGenerator(original, result);

		Diff diff = generator.compute();

		return diff;
	}

	private Diff compute() {
		Diff diffResult = new Diff();

		String[] originalArray = original.split("");
		String[] resultArray = result.split("");

		org.incava.util.diff.Diff diff = new org.incava.util.diff.Diff(
				originalArray, resultArray);
		List<Difference> differences = diff.diff();

		for (Difference difference : differences) {

			if (difference.getDeletedEnd() != -1
					&& difference.getAddedEnd() != -1) {
				// Replacement
				DiffElement element = createReplacementElement(difference);

				diffResult.addElement(element);

			} else if (difference.getAddedEnd() != -1) {
				// addition
				DiffElement element = createAdditionElement(difference);

				diffResult.addElement(element);

			} else if (difference.getDeletedEnd() != -1) {
				// removal
				DiffElement element = createRemovalElement(difference);

				diffResult.addElement(element);

			} else {
				throw new RuntimeException(
						"unhandled case. not supposed to append");
			}

		}

		return diffResult;
	}

	private String getContent(String element, Integer start, Integer end) {
		return element.substring(start - 1, end);
	}

	private AdditionDiffElement createAdditionElement(Difference difference) {
		String content = getContent(result, difference.getAddedStart(),
				difference.getAddedEnd());

		AdditionDiffElement element = new AdditionDiffElement(difference
				.getAddedStart() - 1, content);

		return element;
	}

	private RemovalDiffElement createRemovalElement(Difference difference) {
		String content = getContent(original, difference.getDeletedStart(),
				difference.getDeletedEnd());

		RemovalDiffElement element = new RemovalDiffElement(difference
				.getDeletedStart() - 1, content);

		return element;
	}

	private DiffElement createReplacementElement(Difference difference) {
		RemovalDiffElement removalElement = createRemovalElement(difference);
		AdditionDiffElement additionElement = createAdditionElement(difference);

		ReplacementDiffElement element = new ReplacementDiffElement(
				removalElement, additionElement);

		return element;
	}

}
